package it.infodea.tapestrydea.services;

import it.infodea.tapestrydea.services.jcr.FileIconFactory;
import it.infodea.tapestrydea.services.jcr.IJcrConstants;
import it.infodea.tapestrydea.services.jcr.JcrAssetStreamer;
import it.infodea.tapestrydea.services.jcr.JcrSessionProviderService;
import it.infodea.tapestrydea.services.jcr.JcrWorkspaceService;
import it.infodea.tapestrydea.services.jcr.RepositoryAccessService;
import it.infodea.tapestrydea.services.jcr.impl.FileIconFactoryImpl;
import it.infodea.tapestrydea.services.jcr.impl.JcrAssetStreamerImpl;
import it.infodea.tapestrydea.services.jcr.impl.JcrSessionProviderServiceImpl;
import it.infodea.tapestrydea.services.jcr.impl.JcrWorkspaceServiceImpl;
import it.infodea.tapestrydea.services.jcr.impl.RepositoryAccessServiceImpl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.jcr.Repository;
import javax.jcr.Workspace;
import javax.naming.Context;
import javax.naming.InitialContext;

import org.apache.jackrabbit.api.JackrabbitNodeTypeManager;
import org.apache.jackrabbit.core.jndi.RegistryHelper;
import org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl;
import org.apache.tapestry5.Asset;
import org.apache.tapestry5.annotations.Path;
import org.apache.tapestry5.internal.services.ResourceCache;
import org.apache.tapestry5.ioc.Configuration;
import org.apache.tapestry5.ioc.MappedConfiguration;
import org.apache.tapestry5.ioc.OrderedConfiguration;
import org.apache.tapestry5.ioc.ServiceBinder;
import org.apache.tapestry5.ioc.annotations.Autobuild;
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.ioc.internal.util.TapestryException;
import org.apache.tapestry5.services.BeanBlockContribution;
import org.apache.tapestry5.services.Dispatcher;
import org.apache.tapestry5.upload.services.UploadedFile;

/**
 * Modulo principale dell'applicazione WEB Medis.
 * 
 * Memo:
 * 
 * @Validate("maxlength=255") @Width(value = 80) @Column(length = 255)
 * @Validate("maxlength=4095") @DataType(value = "largetext") @Column(length =
 *                             4095)
 * @Validate("maxlength=2047") @DataType(value = "mediumtext") @Column(length =
 *                             2047)
 * @Validate("maxlength=1023") @DataType(value = "smalltext") @Column(length =
 *                             1023)
 */

public class JcrModule {

	/**
	 * In questo metodo sono definiti i servizi utilizzati all'interno
	 * dell'applicazione. In particolare, sono configurate le associazioni tra i
	 * servizi e le loro implementazioni.
	 * 
	 * @param binder
	 */
	public static void bind(ServiceBinder binder) {
		//Servizi per l'accesso al JCR
		binder.bind(RepositoryAccessService.class, RepositoryAccessServiceImpl.class);
		binder.bind(JcrSessionProviderService.class, JcrSessionProviderServiceImpl.class);
		binder.bind(JcrWorkspaceService.class, JcrWorkspaceServiceImpl.class);
		binder.bind(FileIconFactory.class, FileIconFactoryImpl.class);
	}

	/**
	 * Aggiunge nuovi tipi ai data type di base di Tapestry. The data type are
	 * used to select an editor (or display) block. Normally, the data type is
	 * determined from the type of the property (for example, property type
	 * java.lang.String would map to data type "text").
	 * 
	 * @param configuration
	 */
	@SuppressWarnings("unchecked")
	public void contributeDefaultDataTypeAnalyzer(MappedConfiguration<Class, String> configuration) {
		configuration.add(UploadedFile.class, "file");
	}

	/**
	 * In questo metodo sono configurati nuovi editor (o display) block
	 * utilizzati dal componente BeanEditor (o BeanDisplay)
	 * 
	 * @param configuration
	 */
	public void contributeBeanBlockSource(Configuration<BeanBlockContribution> configuration) {
		addEditBlock(configuration, "file", "FilePropertyEditBlocks");
		addDisplayBlock(configuration, "file", "FilePropertyDisplayBlocks");
		addEditBlock(configuration, "dfile", "FilePropertyEditBlocks");
	}

	private void addEditBlock(Configuration<BeanBlockContribution> configuration, String dataType, String pageName) {
		configuration.add(new BeanBlockContribution(dataType, pageName, dataType, true));
	}

	private void addDisplayBlock(Configuration<BeanBlockContribution> configuration, String dataType, String pageName) {
		configuration.add(new BeanBlockContribution(dataType, pageName, dataType, false));
	}

	/**
	 * Aggiunge gli asset delle icone per i files da scaricare viene usato
	 * principalmente dal simplecontentviewer
	 * 
	 * @param configuration
	 */
	public void contributeFileIconFactory(MappedConfiguration<String, Asset> configuration,
			@Path("classpath:/it/infodea/tapestrydea/assets/images/files/pdf.png") final Asset pdfAsset,
			@Path("classpath:/it/infodea/tapestrydea/assets/images/files/nn.gif") final Asset nnAsset,
			@Path("classpath:/it/infodea/tapestrydea/assets/images/files/jpg.png") final Asset jpgAsset,
			@Path("classpath:/it/infodea/tapestrydea/assets/images/files/txt.png") final Asset txtAsset) {
		configuration.add("application/pdf", pdfAsset);
		configuration.add("text/plain", txtAsset);
		configuration.add("application/octet-stream", nnAsset);
	}

	public static void contributeRegistryStartup(OrderedConfiguration<Runnable> configuration,
			final @Symbol(Context.PROVIDER_URL) String providerUrl, final @Symbol(IJcrConstants.MEDIS_JCR_REPOSITORY_NAME) String repName,
			final @Symbol(IJcrConstants.MEDIS_JCR_REPOSITORY_FILE_PATH_KEY) String repFilePath,
			final @Symbol(IJcrConstants.MEDIS_JCR_REPOSITORY_PATH_KEY) String repHomePath) {
		checkRepositoryPath(repHomePath, repFilePath);
		configuration.add("JackrabbitLoader", new Runnable() {
			public void run() {
				InitialContext ctx;
				try {
					ctx = new InitialContext(IJcrConstants.provideJndiEnv(providerUrl));
					RegistryHelper.registerRepository(ctx, repName, repFilePath, repHomePath, true);
					Repository r = (Repository) ctx.lookup(repName);
					assert (r != null);
					Workspace ws = r.login().getWorkspace();
					NodeTypeManagerImpl ntTypeMgr = (NodeTypeManagerImpl) ws.getNodeTypeManager();
					InputStream nodeTypeStream = JcrModule.class.getResourceAsStream("/conf/dea.xml");
					ntTypeMgr.registerNodeTypes(nodeTypeStream, JackrabbitNodeTypeManager.TEXT_XML, true);
				} catch (Exception e) {
					throw new TapestryException(e.getMessage(), e);
				}
			}
		});
	}

	public static void contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration) {
		configuration.addInstance("AssetRep", RepositoryAssetDispatcher.class, "before:Asset");
	}

	public static JcrAssetStreamer buildJcrAssetStreamer(@Autobuild JcrAssetStreamerImpl service, ResourceCache cache) {
		cache.addInvalidationListener(service);
		return service;
	}

	private static void checkRepositoryPath(String repHomePath, String repFilePath) {
		File repFile = new File(repFilePath);
		File repHome = new File(repHomePath);
		if (!repHome.exists()) {
			repHome.mkdirs();
		}
		if (!repFile.exists()) {
			InputStream resourceAsStream = null;
			FileOutputStream fileOut = null;
			try {
				repFile.createNewFile();
				resourceAsStream = JcrModule.class.getResourceAsStream("/repository/repository.xml");
				byte[] bytes = new byte[resourceAsStream.available()];
				resourceAsStream.read(bytes);
				fileOut = new FileOutputStream(repFile);
				fileOut.write(bytes);
			} catch (IOException e) {
				throw new TapestryException(e.getMessage(), e);
			} finally {
				try {
					if (resourceAsStream != null) {
						resourceAsStream.close();
					}
					if (fileOut != null) {
						fileOut.close();
					}
				} catch (IOException e) {
					throw new TapestryException(e.getMessage(), e);
				}
			}
		}
	}
}
